package com.tiancheng.onecode.trade;

import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.core.type.TypeReference;
import com.tiancheng.onecode.exception.ApiException;
import com.tiancheng.onecode.model.dto.PlatformApplicationDTO;
import com.tiancheng.onecode.model.request.ApplicationAccessTokenRequestVO;
import com.tiancheng.onecode.model.response.ApplicationAccessTokenResponseVO;
import com.tiancheng.onecode.model.response.Result;
import com.tiancheng.onecode.trade.configuration.TradeApplicationProperties;
import com.tiancheng.onecode.trade.enums.TradeInterfaceEnums;
import com.tiancheng.onecode.trade.model.request.*;
import com.tiancheng.onecode.trade.model.request.order.*;
import com.tiancheng.onecode.trade.model.response.*;
import com.tiancheng.onecode.trade.model.response.OpenOrderPrePayResVO;
import com.tiancheng.onecode.trade.utils.ForestHttpUtil;
import com.tiancheng.onecode.utils.AppAccessTokenSignatureUtils;
import com.tiancheng.onecode.utils.JsonUtil;
import com.tiancheng.onecode.utils.PlatformApplicationUtils;
import io.micrometer.common.util.StringUtils;
import org.apache.http.Header;
import org.apache.http.message.BasicHeader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;

/**
 * @Author: kellen
 * @Description: 交易API工具类
 * @Date: create in 2024/12/9 16:25
 */
public class TradeApiClient {
    private static final Logger log = LoggerFactory.getLogger(TradeApiClient.class);

    private final TradeApplicationProperties applicationProperties;

    public TradeApiClient(TradeApplicationProperties applicationProperties) {
        this.applicationProperties = applicationProperties;
    }

    /**
     * 创建订单
     *
     * @param orderBaseVO
     */
    public void createOrder(OpenCreateOrderBaseVO orderBaseVO) {
        final String resultStr = ForestHttpUtil.getInstance().doPostJson(applicationProperties.getHosts() + TradeInterfaceEnums.order_create.getCode(), JSONObject.parseObject(JSONObject.toJSONString(orderBaseVO)), postHeader());
        Map map = resultConvert(resultStr, new TypeReference<Map>() {
        }, TradeInterfaceEnums.order_create);
    }

    /**
     * 取消订单
     *
     * @param orderBaseVO
     */
    public Result closeOrder(OpenCancelOrderVO orderBaseVO) {
        final String resultStr = ForestHttpUtil.getInstance().doPostJson(applicationProperties.getHosts() + TradeInterfaceEnums.order_close.getCode(), orderBaseVO, postHeader());
        final Result result = JsonUtil.fromJson(resultStr, Result.class);
        return result;
    }


    /**
     * 支付订单
     *
     * @param orderPayVO 订单支付参数
     */
    public OpenOrderPrePayResVO pay(OpenOrderPayVO orderPayVO) {
        final String resultStr = ForestHttpUtil.getInstance().doPostJson(applicationProperties.getHosts() + TradeInterfaceEnums.order_pay.getCode(), orderPayVO, postHeader());
        return resultConvert(resultStr, new TypeReference<OpenOrderPrePayResVO>() {
        }, TradeInterfaceEnums.order_pay);

    }

    /**
     * 支付订单
     *
     * @param openRefundVO 订单退款参数
     */
    public OpenRefundCreateResVO refund(OpenRefundVO openRefundVO) {
        final String resultStr = ForestHttpUtil.getInstance().doPostJson(applicationProperties.getHosts() + TradeInterfaceEnums.order_refund.getCode(), JSONObject.parseObject(JSONObject.toJSONString(openRefundVO)), postHeader());
        return resultConvert(resultStr, new TypeReference<OpenRefundCreateResVO>() {
        }, TradeInterfaceEnums.order_refund);

    }

    /**
     * 订单核销
     *
     * @param verificationOrderVO 订单核销参数
     */
    public OpenVerificationResVO verification(OpenVerificationOrderVO verificationOrderVO) {
        final String resultStr = ForestHttpUtil.getInstance().doPostJson(applicationProperties.getHosts() + TradeInterfaceEnums.order_verification.getCode(), verificationOrderVO, postHeader());
        return resultConvert(resultStr, new TypeReference<OpenVerificationResVO>() {
        }, TradeInterfaceEnums.order_verification);
    }

    /**
     * 查询营业地区信息
     *
     * @param dataType 订单核销参数
     */
    public String regionData(String dataType) {
        Map<String, String> params = new HashMap<>();
        params.put("dataType", dataType);
        final String resultStr = ForestHttpUtil.getInstance().get(applicationProperties.getHosts() + TradeInterfaceEnums.merchant_region_data.getCode(), params, getHeader());
        return resultConvert(resultStr, new TypeReference<String>() {
        }, TradeInterfaceEnums.merchant_region_data);
    }

    /**
     * 查询营业地区信息
     *
     * @param parentAreaCode 父级地区编码
     */
    public List<QueryAreaRes> queryByParentAreaCode(String parentAreaCode) {
        Map<String, String> params = new HashMap<>();
        params.put("parentAreaCode", parentAreaCode);
        final String resultStr = ForestHttpUtil.getInstance().get(applicationProperties.getHosts() + TradeInterfaceEnums.merchant_get_region.getCode(), params, getHeader());
        return resultConvert(resultStr, new TypeReference<List<QueryAreaRes>>() {
        }, TradeInterfaceEnums.merchant_get_region);
    }


    /**
     * 查询营业地区信息
     *
     * @param areaCodes 地区编码，英文逗号拼接
     */
    public List<QueryAreaRes> queryRegionByCodes(String areaCodes) {
        Map<String, String> params = new HashMap<>();
        params.put("areaCodes", areaCodes);
        final String resultStr = ForestHttpUtil.getInstance().get(applicationProperties.getHosts() + TradeInterfaceEnums.merchant_query_region_by_codes.getCode(), params, getHeader());
        return resultConvert(resultStr, new TypeReference<List<QueryAreaRes>>() {
        }, TradeInterfaceEnums.merchant_query_region_by_codes);
    }


    /**
     * 查询行业信息信息
     *
     * @param mccName 行业名称
     */
    public List<QueryMccRes> mccList(String mccName) {
        Map<String, String> params = new HashMap<>();
        params.put("mccName", mccName);
        final String resultStr = ForestHttpUtil.getInstance().get(applicationProperties.getHosts() + TradeInterfaceEnums.merchant_mcc_list.getCode(), params, getHeader());
        return resultConvert(resultStr, new TypeReference<List<QueryMccRes>>() {
        }, TradeInterfaceEnums.merchant_mcc_list);
    }

    /**
     * 查询行业信息信息
     *
     * @param mccCode 行业编码
     */
    public List<QueryMccRes> queryMccByCode(String mccCode) {
        Map<String, String> params = new HashMap<>();
        params.put("mccCode", mccCode);
        final String resultStr = ForestHttpUtil.getInstance().get(applicationProperties.getHosts() + TradeInterfaceEnums.merchant_mcc_query_by_code.getCode(), params, getHeader());
        return resultConvert(resultStr, new TypeReference<List<QueryMccRes>>() {
        }, TradeInterfaceEnums.merchant_mcc_query_by_code);
    }


    /**
     * 证件照上传
     *
     * @param file 文件信息
     */
    public MerchantPayRegFileResVO upload(MultipartFile file, String documentType) {
        final PlatformApplicationDTO application = PlatformApplicationUtils.getApplication();
        Header[] headers = new BasicHeader[]{new BasicHeader("token", getAccessToken(application.getClientId(), application.getClientSecret())), new BasicHeader("content-type", "multipart/form-data")};
        Map<String, Object> params = new HashMap<>();
        params.put("documentType", documentType);
        final String resultStr = ForestHttpUtil.getInstance().multipartForm(applicationProperties.getHosts() + TradeInterfaceEnums.merchant_upload.getCode(), file, params, headers);
        return resultConvert(resultStr, new TypeReference<MerchantPayRegFileResVO>() {
        }, TradeInterfaceEnums.merchant_upload);
    }


    /**
     * 商户进件资料查询
     *
     * @return
     */
    public List<MerchantPayRegFileResVO> merchantUploadQuery(List<String> fileIds) {
        Map<String, Object> params = new HashMap<>();
        params.put("fileIds", fileIds);
        final String resultStr = ForestHttpUtil.getInstance().doPost(applicationProperties.getHosts() + TradeInterfaceEnums.merchant_upload_query.getCode(), params, postHeader());
        return resultConvert(resultStr, new TypeReference<List<MerchantPayRegFileResVO>>() {
        }, TradeInterfaceEnums.merchant_upload_query);
    }

    /**
     * 提交商户进件
     *
     * @return
     */
    public MerchantRegAllInfoRes registerMerchant(RegisterMerchantReqVO registerMerchantReqVO) {
        final String resultStr = ForestHttpUtil.getInstance().doPostJson(applicationProperties.getHosts() + TradeInterfaceEnums.merchant_register_merchant.getCode(), registerMerchantReqVO, postHeader());
        return resultConvert(resultStr, new TypeReference<MerchantRegAllInfoRes>() {
        }, TradeInterfaceEnums.merchant_register_merchant);
    }


    /**
     * 提交商户进件
     *
     * @return
     */
    public MerchantRegAllInfoNewRes registerMerchantNew(RegisterMerchantReqNewVO registerMerchantReqVO) {
        final String resultStr = ForestHttpUtil.getInstance().doPostJson(applicationProperties.getHosts() + TradeInterfaceEnums.merchant_register_merchant_new.getCode(), registerMerchantReqVO, postHeader());
        return resultConvert(resultStr, new TypeReference<MerchantRegAllInfoNewRes>() {
        }, TradeInterfaceEnums.merchant_register_merchant_new);
    }


    /**
     * 根据社会信用代码查询是否有进件
     *
     * @param shopLic
     * @return
     */
    public MerchantRegAllInfoRes queryRegisterMerchant(String shopLic) {
        final PlatformApplicationDTO application = PlatformApplicationUtils.getApplication();
        final Map<String, String> params = new HashMap<>();
        params.put("shopLic", shopLic);
        params.put("orgPaymentChannelId", application.getOrgPaymentChannelId().toString());
        final String resultStr = ForestHttpUtil.getInstance().get(applicationProperties.getHosts() + TradeInterfaceEnums.merchant_query_register_merchant.getCode(), params, getHeader());
        return resultConvert(resultStr, new TypeReference<MerchantRegAllInfoRes>() {
        }, TradeInterfaceEnums.merchant_upload);
    }

    /**
     * 根据社会信用代码查询是否有进件
     *
     * @param tradeMerchantPayRegId
     * @return
     */
    public MerchantRegStatusQueryRes queryRegisterMerchantByTradeRegId(Long tradeMerchantPayRegId) {
        final PlatformApplicationDTO application = PlatformApplicationUtils.getApplication();
        final Map<String, Object> params = new HashMap<>();
        params.put("tradeMerchantPayRegId", tradeMerchantPayRegId);
        final String resultStr = ForestHttpUtil.getInstance().get(applicationProperties.getHosts() + TradeInterfaceEnums.merchant_query_register_merchant_status.getCode(), params, getHeader());
        return resultConvert(resultStr, new TypeReference<MerchantRegStatusQueryRes>() {
        }, TradeInterfaceEnums.merchant_upload);
    }

    /**
     * 开户行列表查询
     *
     * @param params
     * @return
     */
    public List<MerchantBranchBankListResVO> branchBankList(MerchantBranchBankListReqVO params) {
        final String resultStr = ForestHttpUtil.getInstance().doPostJson(applicationProperties.getHosts() + TradeInterfaceEnums.merchant_branch_bank_list.getCode(), params, postHeader());
        return resultConvert(resultStr, new TypeReference<List<MerchantBranchBankListResVO>>() {
        }, TradeInterfaceEnums.merchant_branch_bank_list);
    }

    /**
     * 设置商户清分规则
     *
     * @param params 规则信息
     */
    public void setMerchantSettleRule(MerchantPayRegSettleRuleReqVO params) {
        final String resultStr = ForestHttpUtil.getInstance().doPostJson(applicationProperties.getHosts() + TradeInterfaceEnums.merchant_settle_rule.getCode(), params, postHeader());
        String s = resultConvert(resultStr, new TypeReference<String>() {
        }, TradeInterfaceEnums.merchant_branch_bank_list);
    }

    /**
     * 申请对公账户验证
     *
     * @param params
     */
    public RequestAccountVerifyResVO requestAccountVerify(RequestAccountVerifyReq params) {
        final String resultStr = ForestHttpUtil.getInstance().doPostJson(applicationProperties.getHosts() + TradeInterfaceEnums.merchant_request_account_verify.getCode(), params, postHeader());
        return resultConvert(resultStr, new TypeReference<RequestAccountVerifyResVO>() {
        }, TradeInterfaceEnums.merchant_request_account_verify);
    }

    /**
     * 对公账户认证-金额验证
     *
     * @param params
     */
    public CompanyAccountVerifyResVO companyAccountVerify(RequestCompanyAccountVerifyRequest params) {
        final String resultStr = ForestHttpUtil.getInstance().doPostJson(applicationProperties.getHosts() + TradeInterfaceEnums.merchant_company_account_verify.getCode(), params, postHeader());
        return resultConvert(resultStr, new TypeReference<CompanyAccountVerifyResVO>() {
        }, TradeInterfaceEnums.merchant_company_account_verify);
    }

    /**
     * 前台签约
     *
     * @param params
     */
    public MerchantAgreementSignResVO agreementSign(MerchantPayRegAgreementSignReq params) {
        final String resultStr = ForestHttpUtil.getInstance().doPostJson(applicationProperties.getHosts() + TradeInterfaceEnums.merchant_agreement_sign.getCode(), params, postHeader());
        return resultConvert(resultStr, new TypeReference<MerchantAgreementSignResVO>() {
        }, TradeInterfaceEnums.merchant_agreement_sign);
    }


    /**
     * 删除文件
     * @param fileId
     */
    public void deleteFile(String fileId) {
        final Map<String, String> params = new HashMap<>();
        params.put("fileId", fileId);
        final String resultStr = ForestHttpUtil.getInstance().get(applicationProperties.getHosts() + TradeInterfaceEnums.merchant_upload_delete.getCode(), params, getHeader());
        resultConvert(resultStr, new TypeReference<String>() {
        }, TradeInterfaceEnums.merchant_upload_delete);
    }

    /**
     * 获取授权token
     *
     * @return
     */
    public String getAccessToken(String clientId, String clientSecret) {
        // TradeCache.delAccessToken(clientId);
        final Optional<ApplicationAccessTokenResponseVO> accessToken = TradeCache.getAccessToken(clientId);
        if (accessToken.isPresent() && accessToken.get().getExpiresIn() > 5 * 60) {
            // 过期时间大于5分钟，直接使用
            return accessToken.get().getAccessToken();
        }
        for (int i = 0; i < 3; i++) {
            final String token = newToken(clientId, clientSecret);
            if (StringUtils.isNotEmpty(token)) {
                return token;
            }
        }
        // 重试结束，还是没有获取到token
        log.error("重试3次结束，没有获取到token!");
        return null;
    }


    private String newToken(String clientId, String clientSecret) {
        ApplicationAccessTokenRequestVO params = new ApplicationAccessTokenRequestVO();
        params.setClientId(clientId);
        params.setRand(UUID.randomUUID().toString().substring(0, 8));
        params.setTimestamp(System.currentTimeMillis());
        params.setSignature(AppAccessTokenSignatureUtils.signature(params.getClientId(), params.getTimestamp().toString(), params.getRand(), clientSecret));
        Header[] headers = new BasicHeader[]{new BasicHeader("content-type", "application/json")};
        final String resultStr = ForestHttpUtil.getInstance().doPostJson(applicationProperties.getHosts() + TradeInterfaceEnums.auth_token.getCode(), beforePost(params), headers);
        final ApplicationAccessTokenResponseVO result = resultConvert(resultStr, new TypeReference<ApplicationAccessTokenResponseVO>() {
        }, TradeInterfaceEnums.auth_token);
        if (null != result) {
            TradeCache.setAccessToken(params.getClientId(), result);
            return result.getAccessToken();
        }
        return null;
    }

    private <T extends BaseTradeReq> T beforePost(T t) {
        t.setClientSecret(null);
        return t;
    }

    private <T> T resultConvert(String resultStr, TypeReference<T> reference, TradeInterfaceEnums interfaceEnums) {
        log.info("调用交易接口：{} 返回：{}", interfaceEnums.getName(), resultStr);
        if (StringUtils.isNotEmpty(resultStr)) {
            final Result result = JsonUtil.fromJson(resultStr, Result.class);
            if (result.getRet() == 0) {
                return JsonUtil.fromJson(result.getData(), reference);
            }
            log.error("交易接口调用返回失败:{}", result);
            throw new ApiException("交易接口调用失败：" + (StringUtils.isNotEmpty(result.getMsg()) ? result.getMsg() : ""));
        }
        return null;
    }

    private Header[] getHeader() {
        final PlatformApplicationDTO application = PlatformApplicationUtils.getApplication();
        return new BasicHeader[]{new BasicHeader("token", getAccessToken(application.getClientId(), application.getClientSecret()))};
    }

    private Header[] postHeader() {
        final PlatformApplicationDTO application = PlatformApplicationUtils.getApplication();
        return new BasicHeader[]{new BasicHeader("token", getAccessToken(application.getClientId(), application.getClientSecret())), new BasicHeader("content-type", "application/json")};
    }
}
